'''
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http:#www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
'''
from base.datatypes.xsd.XSDDatatype import XSDDatatype
from base.datatypes.xsd.XSDDatatypeInstances import XSDDatatypeInstances
from base.exception.CannotEncodeCharacterException import CannotEncodeCharacterException
from base.java.Objects import Objects
from base.java.StringBuilder import StringBuilder
from base.util.XMLChar import XMLChar

#import java.util.Objects;
#import java.util.regex.Matcher;
#import java.util.regex.Pattern;

#import org.apache.jena.JenaRuntime;
#import org.apache.jena.datatypes.RDFDatatype;
#import org.apache.jena.datatypes.xsd.XSDDatatype;
#import org.apache.jena.graph.Node;
#import org.apache.jena.rdf.model.Literal;
#import org.apache.jena.shared.CannotEncodeCharacterException;
#import org.apache.jena.util.SplitIRI;
#import org.apache.jena.util.XMLChar;

'''
 * Some utility functions.
'''
from abc import ABC, abstractmethod
import re

class Util(ABC):

    '''
     * Given an absolute URI, determine the split point between the namespace
     * part and the localname part. If there is no valid localname part then the
     * length of the string is returned. The algorithm tries to find the longest
     * NCName at the end of the uri, not immediately preceeded by the first
     * colon in the string.
     * <p>
     * This operation follows XML QName rules which are more complicated than
     * needed for Turtle and TriG.   For example, QName can't start with a digit.
     *
     * @param uri
     * @return the index of the first character of the localname
     * @see SplitIRI
    '''
    # param String uri
    # result static int
    @staticmethod
    def splitNamespaceXML(uri):

        # XML Namespaces 1.0:
        # A qname name is NCName ':' NCName
        # NCName             ::=      NCNameStartChar NCNameChar*
        # NCNameChar         ::=      NameChar - ':'
        # NCNameStartChar    ::=      Letter | '_'
        #
        # XML 1.0
        # NameStartChar      ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] |
        #                        [#xD8-#xF6] | [#xF8-#x2FF] |
        #                        [#x370-#x37D] | [#x37F-#x1FFF] |
        #                        [#x200C-#x200D] | [#x2070-#x218F] |
        #                        [#x2C00-#x2FEF] | [#x3001-#xD7FF] |
        #                        [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
        # NameChar           ::= NameStartChar | "-" | "." | [0-9] | #xB7 |
        #                        [#x0300-#x036F] | [#x203F-#x2040]
        # Name               ::= NameStartChar (NameChar)*

        #char ch
        ch = None
        # int
        lg = len(uri)
        if (lg == 0):
            return 0
        # int
        i = lg - 1
        while i >= 1:
            ch = uri[i]
            if (Util.notNameChar(ch)):
                break
            i = i - 1
        # for ( i >= 1 i--):
        #     ch = uri.charAt(i)
        #     if (notNameChar(ch)) break

        j = i + 1

        if (j >= lg):
            return lg

        # Check we haven't split up a %-encoding.
        if (j >= 2 and uri.charAt(j - 2) == '%'):
            j = j + 1
        if (j >= 1 and uri.charAt(j - 1) == '%'):
            j = j + 2
            if (j > lg):
                # JENA-1941: Protect against overshoot in the case of "%x"
                # at end of a (bad) URI.
                return lg
        

        # Have found the leftmost NCNameChar from the
        # end of the URI string.
        # Now scan forward for an NCNameStartChar
        # The split must start with NCNameStart.
        #for ( j < lg j++):
        while j < lg:
            ch = uri.charAt(j)
#            if (XMLChar.isNCNameStart(ch))
#                break 
            if (XMLChar.isNCNameStart(ch)):
                # "mailto:" is special.
                # split "mailto:me" as "mailto:m" and "e" !
                # Keep part after mailto: with at least one character.
                if (j == 7 and uri.startsWith("mailto:")):
                    # Don't split at "mailto:"
                    continue
                else:
                    break
            j = j + 1

        return j
    

    '''
     * answer true iff this is not a legal NCName character, ie, is
     * a possible split-point start.
    '''
    # param char ch
    # result static boolean
    @staticmethod
    def notNameChar(ch):
        return not XMLChar.isNCName(ch)
    

     #protected static Pattern
    standardEntities = r"&|<|>|\t|\n|\r|\'|\""

    # param String s
    # result static String
    @staticmethod
    def substituteStandardEntities(self, s):
        #if (standardEntities.matcher(s).find()):
        if re.search(Util.standardEntities, s) is not None:
            return Util.substituteEntitiesInElementContent(s).replace("'", "&apos").replace("\t", "&#9").replace("\n", "&#xA").replace("\r", "&#xD").replace("\"", "&quot")
                    
        else:
            return s
    

    # protected static Pattern
    entityValueEntities = r"&|%|\'|\""

    # param String s
    # result static String
    @staticmethod
    def substituteEntitiesInEntityValue(s):
        #if (entityValueEntities.matcher(s).find()):
        if re.search(Util.entityValueEntities, s) is not None:
            return s.replace("&", "&amp").replace("'", "&apos").replace("%", "&#37").replace("\"", "&quot")
                    
        else:
            return s
    

    elementContentEntities = r"<|>|&|[\0-\37and[^\n\t]]|\uFFFF|\uFFFE"

    '''
     * Answer <code>s</code> modified to replace &lt, &gt, and &amp by
     * their corresponding entity references.
     *
     * <p>
     * Implementation note: as a (possibly misguided) performance hack,
     * the obvious cascade of replace calls is replaced by an explicit
     * loop that looks for all three special characters at once.
    '''
    # param String s
    # result static String
    @staticmethod
    def substituteEntitiesInElementContent(s):
        #Matcher m = elementContentEntities.matcher(s)
        #if (!m.find())
        if re.search(Util.elementContentEntities, s) is None:
            return s
        else:
            # int
            start = 0
            # StringBuilder
            result = StringBuilder('')
            r = re.search(Util.elementContentEntities, s)
            while r:
                result.append(s[start, r.start()])
                ch = s[r.start()]
                if ch == '\r':
                    result.append('&#xD')
                elif ch == '<':
                    result.append('&lt')
                elif ch == '&':
                    result.append('&amp')
                elif ch == '>':
                    result.append('&gt')
                else:
                    raise CannotEncodeCharacterException(ch, 'XML')
                start += 1
                r = re.search(Util.elementContentEntities, s, start)
            # do:
            #     result.append(s.substring(start, m.start()))
            #     char ch = s.charAt(m.start())
            #     if ch == '\r':
            #         result.append('&#xD')
            #     elif ch == '<':
            #         result.append('&lt')
            #     elif ch == '&':
            #         result.append('&amp')
            #     elif ch == '>':
            #         result.append('&gt')
            #     else:
            #         raise CannotEncodeCharacterException(ch, 'XML')
            #     # switch (ch):
            #     #     case '\r':
            #     #         result.append("&#xD")
            #     #         break
            #     #     case '<':
            #     #         result.append("&lt")
            #     #         break
            #     #     case '&':
            #     #         result.append("&amp")
            #     #         break
            #     #     case '>':
            #     #         result.append("&gt")
            #     #         break
            #     #     default:
            #     #         throw new CannotEncodeCharacterException(ch, "XML")
            #     start = m.end()
            # while (m.find(start))
            result.append(s[start:-1])
            return result.toString()
        
    

    # param String s, String oldString, String newString
    # result static String
    @staticmethod
    def replace(s, oldString, newString):
        return s.replace(oldString, newString)
    

    '''
     * A Node is a simple string if:
     * <ul>
     * <li>(RDF 1.0) No datatype and no language tag.
     * <li>(RDF 1.1) xsd:string
     * </ul>
    '''
    # param Node n
    # result static boolean
    @staticmethod
    def isSimpleString(n):
        Objects.requireNonNull(n, 'node can not be null')
        if (not n.isLiteral()):
            return False
        # RDFDatatype
        dt = n.getLiteralDatatype()
        if (dt is None):
            return not Util.isLangString(n)
        # if (JenaRuntime.isRDF11)
        return dt.equals(XSDDatatypeInstances.XSDstring)
        # return false
    

    '''
     * A Node is a language string if it has a language tag.
     * (RDF 1.0 and RDF 1.1)
    '''
    # param Node n
    # result static boolean
    @staticmethod
    def isLangString(n):
        Objects.requireNonNull(n, 'node can not be null')
        if not n.isLiteral():
            return False
        # String
        lang = n.getLiteralLanguage()
        if (lang is None):
            return False
        return not lang.equals("")
    

    '''
     * Return true if the literal is a simple string.
     * <p>RDF 1.0:@literal => it is a plain literal, with no language tag
     * <p>RDF 1.1:@literal => it has datatype xsd:string
    '''
    # param Literal lit
    # result static boolean
    @staticmethod
    def isSimpleStringLit(lit):
        Objects.requireNonNull(lit, 'literal can not be null')
        # String
        dtStr = lit.getDatatypeURI()
        if (dtStr is None):
            return not Util.isLangStringLit(lit)
        #if (JenaRuntime.isRDF11)
        return dtStr.equals(XSDDatatypeInstances.XSDstring.getURI())
        #return false
    

    '''
     * Return true if the literal has a language tag. (RDF 1.0 and RDF 1.1)
    '''
    # param Literal lit
    # result static boolean
    @staticmethod
    def isLangStringLit(lit):
        Objects.requireNonNull(lit, 'literal can not be null')
        # String
        lang = lit.getLanguage()
        if (lang is None):
            return False
        return not lang.equals("")

    

